﻿#include "DevRunner.h"
#include "public/public.h"
#include "realtimedb.h"
#include "SysRuntimeEvent.h"
#include "DevicePluginManager.h"
#include "Port/ComPort.h"
#include "Port/NetPort.h"
#include "Port/SimPort.h"
#include "Port/DdsPort.h"
#include <QTextStream>
#include <QTextCodec>
#include <QMutexLocker>
#include "qprojectcore.h"
#include <QMessageBox>
#include <QDebug>
#include "projdata/Tag.h"



DevRunner::DevRunner(QObject *parent)
    : QObject(parent),
      projCore(NULL)
{

}

DevRunner::~DevRunner()
{

}

void DevRunner::AddPortName(const QString name)
{
    qDebug() << "DevRunner::AddPortName:PortCurrent-" << name;
    foreach(QString port, m_listPortName) {
        qDebug() << "DevRunner::AddPortName:PortList-" << port;
        if(name == port) {
            return;
        }
    }
    m_listPortName.append(name);
}

RunTimeTag *DevRunner::createRunTimeTag(Tag *pTagObj)
{
    RunTimeTag *pRtTagObj = new RunTimeTag(NULL);
    pRtTagObj->id = pTagObj->m_id;
    pRtTagObj->blockReadID = pTagObj->m_blockReadId;
    pRtTagObj->name = pTagObj->m_name;
    pRtTagObj->unit = pTagObj->m_unit;
    pRtTagObj->addrType = pTagObj->m_addrType;
    pRtTagObj->addrOffset = pTagObj->m_addrOffset.toInt();
    pRtTagObj->aAddrType2 = pTagObj->m_addrType2;
    pRtTagObj->addrOffset2 = pTagObj->m_addrOffset2.toInt();
    QString szDataType = pTagObj->m_dataType;
    pRtTagObj->writeable = (TWriteAble)pTagObj->m_writeable;
    pRtTagObj->remark = pTagObj->m_remark;
    pRtTagObj->ownGroup = pTagObj->m_ownGroup;

    DeviceInfo &deviceInfo = projCore->m_deviceInfo;
    DeviceInfoObject *pDevObj = deviceInfo.getObjectByName(pTagObj->m_devType);
    if(pDevObj) {
        pRtTagObj->devType = pDevObj->m_name;
    }

    pRtTagObj->dataType = TYPE_VARIANT;
    pRtTagObj->bufLength = 1;

    qDebug() << "DevRunner::createRunTimeTag:szDataType:" << szDataType;
    QStringList listBlockRead;
    if(pRtTagObj->blockReadID < 1) {
        // 块读变量 寄存器个数：数据类型
        if(szDataType.indexOf(":") > -1) {
            listBlockRead = szDataType.split(":");
            if(listBlockRead.count() == 2) {
                szDataType = listBlockRead[1];
            }
        }
    }

    if(szDataType == "bool") {
        pRtTagObj->dataType = TYPE_BOOL;
        pRtTagObj->bufLength = 1;
    } else if(szDataType == "int8") {
        pRtTagObj->dataType = TYPE_INT8;
        pRtTagObj->bufLength = 1;
    } else if(szDataType == "uint8") {
        pRtTagObj->dataType = TYPE_UINT8;
        pRtTagObj->bufLength = 1;
    } else if(szDataType == "int16") {
        pRtTagObj->dataType = TYPE_INT16;
        pRtTagObj->bufLength = 2;
    } else if(szDataType == "uint16") {
        pRtTagObj->dataType = TYPE_UINT16;
        pRtTagObj->bufLength = 2;
    } else if(szDataType == "int32") {
        pRtTagObj->dataType = TYPE_INT32;
        pRtTagObj->bufLength = 4;
    } else if(szDataType == "uint32") {
        pRtTagObj->dataType = TYPE_UINT32;
        pRtTagObj->bufLength = 4;
    } else if(szDataType == "int64") {
        pRtTagObj->dataType = TYPE_INT64;
        pRtTagObj->bufLength = 8;
    } else if(szDataType == "uint64") {
        pRtTagObj->dataType = TYPE_UINT64;
        pRtTagObj->bufLength = 8;
    } else if(szDataType == "float32") {
        pRtTagObj->dataType = TYPE_FLOAT32;
        pRtTagObj->bufLength = 4;
    } else if(szDataType == "float64") {
        pRtTagObj->dataType = TYPE_FLOAT64;
        pRtTagObj->bufLength = 8;
    } else if(szDataType == "bcd8") {
        pRtTagObj->dataType = TYPE_BCD8;
        pRtTagObj->bufLength = 1;
    } else if(szDataType == "bcd16") {
        pRtTagObj->dataType = TYPE_BCD16;
        pRtTagObj->bufLength = 2;
    } else if(szDataType == "bcd32") {
        pRtTagObj->dataType = TYPE_BCD32;
        pRtTagObj->bufLength = 3;
    } else if(szDataType == "ascii2char") {
        pRtTagObj->dataType = TYPE_ASCII2CHAR;
        pRtTagObj->bufLength = 2;
    } else if(szDataType == "string") {
        pRtTagObj->dataType = TYPE_STRING;
        pRtTagObj->bufLength = 256;
    } else if(szDataType == "bytes") {
        pRtTagObj->dataType = TYPE_BYTES;
        pRtTagObj->bufLength = 256;
    }

    // 块读变量
    if(listBlockRead.count() == 2) {
        pRtTagObj->dataType = TYPE_BYTES;
        int num = listBlockRead[0].toInt();
        pRtTagObj->bufLength = pRtTagObj->bufLength * num;
        pRtTagObj->isBlockRead = true;
        qDebug() << __FILE__ << __LINE__ << __FUNCTION__ << "num: " << num << ", bufLength: " << pRtTagObj->bufLength;
    }

    pRtTagObj->dataFromDevice = new quint8[pRtTagObj->bufLength];
    memset((void *)pRtTagObj->dataFromDevice, 0, pRtTagObj->bufLength);
    pRtTagObj->dataToDevice = new quint8[pRtTagObj->bufLength + 1]; // 最后一个字节标记需要同步数据至PLC等设备
    memset((void *)pRtTagObj->dataToDevice, 0, pRtTagObj->bufLength + 1);

    return pRtTagObj;
}

bool DevRunner::Load()
{
    qDebug() << "DevRunner::Load:" <<"start load devices!";
    // load devices,devices_framework.ini
    m_Devices.clear();
    DeviceInfo &deviceInfo = projCore->m_deviceInfo;
    qDebug() << "DevRunner::Load:load devices:" << deviceInfo.m_listDeviceInfoObject.size();
    //获取方案中的设备列表清单
    for(int i = 0; i < deviceInfo.m_listDeviceInfoObject.count(); i++) {
        DeviceInfoObject *pObj = deviceInfo.m_listDeviceInfoObject.at(i);
        qDebug() << "DevRunner::Load:load device:info name:" << pObj->m_name;
        qDebug() << "DevRunner::Load:load device:device name:" << pObj->m_deviceName;
        qDebug() << "DevRunner::Load:load device:device type:" << pObj->m_deviceType;
        QString szProtocol = pObj->m_protocol;
        QString szPortType = pObj->m_deviceType;
        QString szDeviceName = pObj->m_name;
        QString szDeviceNameIn = pObj->m_deviceName;
        qDebug() << "Protocol: " << szProtocol
                 << ", PortType:" << szPortType
                 << ", DeviceName:" << szDeviceName;
        Device *pDeviceObj = new Device(projCore);

        IDevicePlugin *pDevicePluginObj = DevicePluginManager::getInstance()->getPlugin(szProtocol);

        if(pDevicePluginObj != NULL) {
            pDeviceObj->m_pDevicePluginObj = pDevicePluginObj;
        }else{
            qDebug() << "DevRunner::Load:GetPlugin-" << szProtocol
                     << "Is Null" ;
        }

        if(szPortType == "COM") {
            ComPort* pComPortObj = new ComPort();
            pDeviceObj->m_pPortObj = pComPortObj;
            ComDevicePrivate *pComDevicePrivateObj = new ComDevicePrivate();

            if(pComDevicePrivateObj->LoadData(szDeviceName, projCore)) {
                QStringList comArgs;
                comArgs << QString().number(pComDevicePrivateObj->m_iBaudrate);
                comArgs << QString().number(pComDevicePrivateObj->m_iDatabit);
                comArgs << pComDevicePrivateObj->m_sVerifybit;
                comArgs << QString().number(pComDevicePrivateObj->m_fStopbit);

                if(!pComPortObj->open(pComDevicePrivateObj->m_sPortNumber, comArgs)) {
                    qCritical() << "ComPort open fail!";
                }
            }

            pDeviceObj->m_pDevicePrivateObj = pComDevicePrivateObj;
        } else if(szPortType == "NET") {
            NetDevicePrivate* pNetDevicePrivateObj = new NetDevicePrivate();

            if(pNetDevicePrivateObj->LoadData(szDeviceName, projCore)) {
                QStringList netArgs;
                netArgs << pNetDevicePrivateObj->m_sIpAddress;
                netArgs << QString().number(pNetDevicePrivateObj->m_iPort);
                NetPort* pNetPortObj = new NetPort();
                pDeviceObj->m_pPortObj = pNetPortObj;

                if(!pNetPortObj->open("Net", netArgs)) {
                    qCritical() << "NetPort open fail!";
                }
            }

            pDeviceObj->m_pDevicePrivateObj = pNetDevicePrivateObj;
        } else if(szPortType == "SIM") {
            //在此绑定Device与Port的关系
            //PortType，为Sim，Protocol：后期可以为算法，调试时为空

            SimDevicePrivate* pSimDevicePrivateObj = new SimDevicePrivate();

            if(pSimDevicePrivateObj->LoadData(szDeviceName, projCore)) {
                QStringList simArgs;
                simArgs << "0";
                simArgs << "1";
                SimPort* pSimPortObj = new SimPort();
                pDeviceObj->m_pPortObj = pSimPortObj;
                if(!pSimPortObj->open("Sim", simArgs)) {
                    qCritical() << "SimPort open fail!";
                }
            }
            pDeviceObj->m_pDevicePrivateObj = pSimDevicePrivateObj;
        } else if(szPortType == "DDS") {
            DdsDevicePrivate* pDdsDevicePrivateObj = new DdsDevicePrivate();

            if(pDdsDevicePrivateObj->LoadData(szDeviceName, projCore)) {
                QStringList netArgs;
                netArgs << pDdsDevicePrivateObj->m_sIpAddress;
                netArgs << QString().number(pDdsDevicePrivateObj->m_iPort);
                DdsPort* pDdsPortObj = new DdsPort();
                pDeviceObj->m_pPortObj = pDdsPortObj;
                if(!pDdsPortObj->open("Dds", netArgs)) {
                    qCritical() << "DdsPort open fail!";
                }
            }
            pDeviceObj->m_pDevicePrivateObj = pDdsDevicePrivateObj;
        }
        m_Devices.append(pDeviceObj);

    }

    /////////////////////////////////////////////

    // 查找已使用的端口名称并添加至启动列表
    qDebug() << "DevRunner::Load:Devices Size Is " << m_Devices.size();
    foreach(Device *pDevice, m_Devices) {
        qDebug() << "DevRunner::Load:Add Port Name " << pDevice->getPortName();
        AddPortName(pDevice->getPortName());
    }

    /////////////////////////////////////////////

    // load block read tags and create rtdb
    qDebug() << "DevRunner::Load:Device Block Read Tag Size Is " << projCore->m_tagMgr.m_mapDevBlockReadTags.size();
    foreach (QString devName, projCore->m_tagMgr.m_mapDevBlockReadTags.keys()) {
        qDebug() << "DevRunner::Load:Process Device " << devName;

        Device *pDevice = FindDevice(devName);
        if(pDevice != NULL) {
            //获取设备Tag
            foreach(Tag *pTagObj, projCore->m_tagMgr.m_mapDevBlockReadTags[devName]) {
                qDebug() << "DevRunner::Load:Now Create Run Time Tag For Dev " << devName;

                RunTimeTag *pRtTagObj = createRunTimeTag(pTagObj);
                if(pRtTagObj) {
                    qDebug() << "DevRunner::Load:Now Insert Run Time Tag For Dev " << devName;

                    RealTimeDB::instance()->rtdb.insert(pRtTagObj->id, pRtTagObj);
                    if(pDevice->m_pDevicePluginObj) {
                        qDebug() << "DevRunner::Load:Now set block read Tag For Dev " << devName;

                        pDevice->m_pDevicePluginObj->setBlockReadTagBufferLength(pRtTagObj);
                    }
                    qDebug() << "DevRunner::Load:Now Add IO Tag For Dev " << devName;

                    pDevice->addIOTagToDeviceTagList(pRtTagObj);
                }
            }
        }else{
            qDebug() << "DevRunner::Load:Device is not Exist!" << devName;

        }
    }
    foreach (QString devNme, projCore->m_tagMgr.m_mapDevBlockReadTags.keys()) {
        qDeleteAll(projCore->m_tagMgr.m_mapDevBlockReadTags[devNme]);
        projCore->m_tagMgr.m_mapDevBlockReadTags[devNme].clear();
    }
    projCore->m_tagMgr.m_mapDevBlockReadTags.clear();
    //在Device里写tag，会触发widget的setSelectedTag?
    // load tags and create rtdb
    qDebug() << "DevRunner::Load:Tags size" << projCore->m_tagMgr.m_vecTags.size();
    foreach(Tag *pTagObj, projCore->m_tagMgr.m_vecTags) {
        qDebug() << "DevRunner::Load:Tag " << pTagObj->toXmlNodeString();
        RunTimeTag *pRtTagObj = createRunTimeTag(pTagObj);
        qDebug() << "DevRunner::Load:Create Run Tag End " ;
        if(pRtTagObj) {
            qDebug() << "DevRunner::Load:Insert Start " << pRtTagObj->toString();

            RealTimeDB::instance()->rtdb.insert(pRtTagObj->id, pRtTagObj);
            qDebug() << "DevRunner::Load:Find Start " << pRtTagObj->devType;
            Device *pDevice = FindDevice(pRtTagObj->devType);
            if(pDevice != NULL) {
                qDebug() << "DevRunner::Load:Insert Start " << pRtTagObj->toString();

                pDevice->addIOTagToDeviceTagList(pRtTagObj);
            }
        }
    }
    qDeleteAll(projCore->m_tagMgr.m_vecTags);
    projCore->m_tagMgr.m_vecTags.clear();

    return true;
}


bool DevRunner::Unload()
{
    qDeleteAll(m_Devices);
    m_Devices.clear();
    qDeleteAll(m_listPortThread);
    m_listPortThread.clear();
    RealTimeDB::instance()->releaseTags();
    return true;
}

void DevRunner::Start()
{
    qDebug() << "DevRunner::Start:" << m_listPortName.size();
    foreach(QString name, m_listPortName) {
        qDebug() << "DevRunner::Start:" << name;
        PortThread *pPortThread = new PortThread(name);

        foreach(Device *pDevice, m_Devices) {
            if(name == pDevice->getPortName()) {
                pPortThread->AddDevice(pDevice);
            }
        }
        m_listPortThread.append(pPortThread);
    }

    foreach(PortThread *pPortThread, m_listPortThread) {
        pPortThread->Start();
    }
}

void DevRunner::Stop()
{
    foreach(PortThread *pPortThread, m_listPortThread) {
        pPortThread->Stop();
    }
}

Device *DevRunner::FindDevice(const QString name)
{
    qDebug() << "DevRunner::FindDevice:Name " << name << "device list size " << m_Devices.size();
    for(int i = 0; i < m_Devices.size(); ++i) {
        Device *pObj = m_Devices.at(i);
        qDebug() << "DevRunner::FindDevice:" << pObj->getDeviceName();
        if(pObj->getDeviceName() == name) {
            return pObj;
        }
    }

    return NULL;
}

bool DevRunner::event(QEvent *event)
{
    if(event->type() == EV_StartRuntime) {
        Load();
        Start();
        qDebug() << "start runtime.";
        return false;
    } else if(event->type() == EV_StopRuntime) {
        qDebug() << "stop runtime.";
        Stop();
        Unload();
        return false;
    } else if(event->type() == EV_RestartRuntime) {
        qDebug() << "restart runtime.";
        Stop();
        Unload();
        Load();
        Start();
        return false;
    }

    return QObject::event(event);
}
